
//https://blog.csdn.net/weixin_51229250/article/details/121445228

/*
一、安装cuda
（一）现在跑windows，unix / linux还没有跑过，暂不提及;
（二）为编程方便，在windows现安装visual statio 2015，当然也可以不安装，如果你对cmd + notePad操作比较熟的话;
（三）到官网上下载咯https://developer.nvidia.com/cuda-toolkit-archive，截至2017-12-18，最新的到9.0了;
（四）安装时，最好选全部安装（需手动选），里面集成了显卡程序等。
二、一些基础
（一）几个概念
并行计算：分为任务并行和数据并行，任务并行如C的多线程多任务运算，cuda是主要适合数据并行。
主机端：即你的CPU，平常程序都在CPU上运行，CPU主要适合处理控制密集型任务，比如从一个函数切换到另外一个函数
设备端：即你的GPU显卡，GPU程序当然有特殊的接口了，主要适合数据并行计算密集型任务，如矩阵运算等 cuda的主要作用就是优化大运算量的计算，到官网上可以查到与CPU的对比，当然你实际测的时候可能会有差别，一般就23倍吧，你实际体验时，可能超过着个值，有时10 - 100都有可能，毕竟CPU跑的东西不单有你的程序。
（二）cuda基础
1.第一个程序：hello.cu(参考hello.cu程序, 下同)
PS：不多讲，直接糊程序吧，边写边讲，这样节省时间，也方便学习
2.CUDA一般编程结构
一般按照结构：程序开始->CPU运行->将CPU数据copy到GPU->GPU运行->将GPU数据copy到CPU->CPU运行->程序结束
当然程序长了，就不是这么简单了。
3.线程管理：checkDimension.cu
(1)从大到小分别为：显卡（GPU）->网格（grid）->线程块（block）->线程（thread），注意从网格开始最大为3维，当然也可以1维了
(2)网格(grid) :一个内核函数就是一个网格，里面所有线程都在这个网格范围内，里面的线程共享全局内存空间
(3)线程块(block)：一个网格可以包含很多个block，block之间可以通过“同步”和“共享内存”进行协作，block之间的区分通过“blockIdx”
(4)线程(thread)：一个线程块可以包含很多个thread，thread之间区分通过threadIdx，当然如果block不一样，threadIdx肯定需要继续区分
(5)blockIdx / threadIdx : 是dim3类型变量（整型），是索引线程的关键，对某个线程的索引：blockIdx.x / y / z, threadIdx.x / y / z
(6)gridDim / blockDim : 也是dim3类型变量，是检查线程维数的光剑，对某个线程所属的网格维数、线程块维数进行检测：gridDim.x / y / z, blockDim.x / y / z
(7)dim3:一个变量类型
如：dim3 dd;//dd.x,dd.y,dd.z 默认为1
dim3 dd(2, 3);//dd.x==2,dd.y==3,dd.z==1
一般我们这样定义：
dim3 grid(2, 3);//就是定义一个网格，里面包含2*3*1个block
dim3 block(4, 5);//就是定义一个线程块，里面包含4*5*1个thread

*/



/* hello.cu：用CPU和GPU在控制台打印“hello,world” */
/* Authored by alpc40 */

#include <stdio.h>
#include<stdlib.h>
//下面两个头文件基本都会用到，别省了
#include "cuda_runtime.h"
#include "device_launch_parameters.h"

//这个宏还是蛮有用,主要功能是检测cuda函数的错误，以方便检查
/*
  1.cudaError_t:CUDA的错误码，它的错误码太多了，大概有80-90个，具体什么错误碰到了再解释
  2.cudaGetErrorString():将CUDA错误码转换成具体错误语句，方便判断程序员错误
  3.__FILE__,__LINE__:这个就不解释了，虽然我也用的不太熟
*/
#define CHECK(call)                                                  \
{                                                                    \
    const cudaError_t err = call;                                    \
    if (err != cudaSuccess)                                          \
    {                                                                \
        fprintf(stderr, "Error: %s:%d, ", __FILE__, __LINE__);       \
        fprintf(stderr, "code: %d, reason: %s\n", err,               \
                cudaGetErrorString(err));                            \
        exit(1);                                                     \
    }                                                                \
}
//下面是内核函数
/*
  __global__:在设备端执行的函数，但是一般在主机端调用，设备端也能调用，但对GPU计算能力必须>3，什么意思，暂不解释
  __device__:在设备端执行且调用的函数，就是说一般的函数调用不了
  __host__：在主机端调用的程序，就是我们一般的代码啦，这个标识是可以省略的
*/
__global__ void helloFromGPU()
{
    printf("Hello World from GPU!\n");
}
//下面是主函数
/*
  <<<grid,block>>>:三个尖括号是cuda特有，是核函数的执行配置，调用核函数必须用到它
      grid是网格，这个值代表调用多少个block；block是线程块，代表调用多少个线程
  cudaDeviceReset()：显示地释放和清空当前进程的GPU资源，别乱用哦，用了的话，GPU数据全没了
*/
int main1(int argc, char** argv)
{
    printf("Hello World from CPU!\n");

    helloFromGPU << <1, 10 >> > ();
    CHECK(cudaDeviceReset());
    return 0;
}
